//package com.gosling.cloudspace.prometheus;
//
//import cn.hutool.core.util.ObjectUtil;
//import cn.hutool.core.util.StrUtil;
//import cn.hutool.json.JSONUtil;
//import com.gosling.cloudspace.common.entity.bo.ExpBO;
//import com.gosling.cloudspace.common.entity.bo.GlobalExceptionBO;
//import com.gosling.cloudspace.common.entity.bo.Groups;
//import com.gosling.cloudspace.common.constants.PrometheusTitleEnum;
//import com.gosling.cloudspace.common.exception.PrometheusException;
//import com.gosling.cloudspace.config.web.ExceptionNoticeProperties;
//import com.gosling.cloudspace.common.annotation.Prometheus;
//import com.gosling.cloudspace.common.constants.Scene;
//import io.swagger.annotations.ApiModelProperty;
//import lombok.extern.slf4j.Slf4j;
//import org.aspectj.lang.JoinPoint;
//import org.aspectj.lang.reflect.MethodSignature;
//import org.springframework.beans.factory.annotation.Value;
//import org.springframework.stereotype.Component;
//import org.springframework.validation.BindingResult;
//import org.springframework.web.context.request.RequestContextHolder;
//import org.springframework.web.context.request.ServletRequestAttributes;
//import org.springframework.web.multipart.MultipartFile;
//
//import javax.annotation.Resource;
//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//import java.lang.reflect.Method;
//import java.util.*;
//
///**
// * 异常链路处理工具
// *
// * @author lihonglin
// * @date 2024/2/23 13:37
// */
//@Slf4j
//@Component
//public class PrometheusHandler {
//
//
//    @Value("${spring.application.name}")
//    private String serverName;
//    //@Resource
//    //mq生产者
//    //private MsgProduce producer;
//    @Resource
//    private ExceptionNoticeProperties exceptionNoticeProperties;
//
//    /***
//     * 业务异常
//     *
//     * 可预知的异常，如：参数校验异常，业务流程中提前被定义的异常等
//     * 这种异常的等级并不是特别高，有些需要业务人员参与关注，而大多数情况下，业务人员不需要关注
//     * 所以这种异常需要控制通知的频率以及通知的范围，避免群消息过于繁多，导致在关键时候找不到想要关注的重要消息
//     *
//     * 1、需要手动在类上添加 @Tag(name = "xxx")注解
//     * 2、在方法上添加 @Operation(summary = "xxx")注解
//     * 3、然后修改抛出的异常为 PrometheusException 例如 throw new PrometheusException("签章异常...");
//     *
//     * Business exception.
//     *
//     * Predictable exceptions, such as parameter check exceptions, exceptions defined in advance in business processes, etc.
//     * The level of this exception is not particularly high, some require business staff to participate in attention, and in most cases, business staff do not need to pay attention to.
//     *
//     * Therefore, this exception needs to control the frequency and scope of notifications to avoid too many group messages, resulting in the failure to find important messages that you want to pay attention to at critical times.
//     *
//     * 1. You need to manually add @ Tag (name = "xxx") annotation to the class.
//     * 2. Add @ Operation (summary = "xxx") to the method.
//     * 3. Then modify the exception thrown to PrometheusException such as throw new PrometheusException ("signature exception.")
//     *
//     * @see //com.soyoo.yooloan.framework.prometheus.core.pojo.GlobalExceptionBO
//     * @see //io.swagger.v3.oas.annotations.tags.Tag
//     * @see //io.swagger.v3.oas.annotations.Operation
//     *
//     * @param point
//     * @param startTime
//     * @param e
//     */
//    @ApiModelProperty("该方法提供给切面使用，手动方式无需调用")
//    public void handlePrometheusExp(JoinPoint point, long startTime, PrometheusException e) {
//        MethodSignature sign = (MethodSignature) point.getSignature();
//        GlobalExceptionBO exp = new GlobalExceptionBO(e, exceptionNoticeProperties);
//        //系统相关参数
//        expSysInfo(point, startTime, exp);
//        //请求相关参数
//        expRequestInfo(sign, exp);
//        //推送至业务飞书群
//        devBusiness(e.getCode(), exp);
//    }
//
//    /***
//     * 该方法用于监听器注解
//     * 由于PrometheusException Exception都有固定的调用方式
//     * 如果加了监听则只对业务异常[ServiceException | ServerException]进行推送
//     *
//     * 作用：避免和由于PrometheusException冲突 重复发送消息
//     *
//     * @param point
//     * @param startTime
//     * @param e
//     */
//    @ApiModelProperty("该方法用于监听器注解@Prometheus")
//    public void handlePrometheusOtherExp(JoinPoint point, long startTime, Exception e) {
//        MethodSignature sign = (MethodSignature) point.getSignature();
//        GlobalExceptionBO exp = new GlobalExceptionBO(e, exceptionNoticeProperties);
//        //系统相关参数
//        expSysInfo(point, startTime, exp);
//        //获取请求IP
//        expIp(exp);
//        //接口描述
//        Prometheus prometheus = sign.getMethod().getAnnotation(Prometheus.class);
//        PrometheusTitleEnum titleEnum = prometheus.title();
//        exp.setUri(prometheus.uri());
//        exp.setCode(titleEnum.getCode());
//        exp.setTitle(titleEnum.getName());
//        exp.setMonitorType(titleEnum.getType());
//        //推送至业务飞书群
//        devBusiness(exp.getCode(), exp);
//    }
//
//    /***
//     * 系统异常
//     *
//     * 无法预估的异常，如：空指针异常，类型转换错误，sql执行异常，超时等
//     * 如果出现这种情况，那么这种异常需要及时处理，异常的等级也是最高的P0级别
//     *
//     * Unpredictable exceptions, such as null pointer exception, type conversion error, sql execution exception, timeout, etc.
//     * If this happens, the exception needs to be handled in time, and the exception level is the highest P0 level.
//     *
//     * @see GlobalExceptionBO
//     *
//     * @param point
//     * @param startTime
//     * @param e
//     */
//    @ApiModelProperty("该方法提供给切面使用，手动方式无需调用")
//    public void handleDefault(JoinPoint point, long startTime, Throwable e) {
//        MethodSignature sign = (MethodSignature) point.getSignature();
//        GlobalExceptionBO exp = new GlobalExceptionBO(e, exceptionNoticeProperties);
//        //系统相关参数
//        expSysInfo(point, startTime, exp);
//        //请求相关参数
//        expRequestInfo(sign, exp);
//        expIp(exp);
//        exp.setCode(PrometheusTitleEnum.OTHER.getCode());
//        exp.setTitle(PrometheusTitleEnum.OTHER.getName());
//        exp.setMonitorType(PrometheusTitleEnum.OTHER.getType());
//        //推送至开发飞书群
//        devSend(exp);
//    }
//
//    private void expIp(GlobalExceptionBO exp) {
//        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        if (ObjectUtil.isNotNull(requestAttributes)) {
//            HttpServletRequest request = requestAttributes.getRequest();
//            exp.setIp(getForwardedIp(request));
//        }
//        //如果都拿不到 则说明是定时任务
//        if (StrUtil.isBlank(exp.getIp())) {
//            exp.setIp("xxlJob");
//        }
//    }
//
//    /***
//     * 手动添加异常监控，发送至飞书
//     * 该方法无需传入功能描述（title），可以自动获取接口名，只需要提前定义好如下两点：
//     * 1、需要手动在类上添加 @Tag(name = "xxx")注解
//     * 2、在方法上添加 @Operation(summary = "xxx")注解
//     *
//     * @param code      错误编码
//     * @param msg       异常信息
//     * @param reqData   请求参数
//     */
//    @ApiModelProperty("手动调用时调用该方法，自动填充title和uri")
//    public void insert(Integer code, String msg, String reqData) {
//        GlobalExceptionBO exp = new GlobalExceptionBO(code, msg, reqData);
//        //系统相关参数
//        exp.setUserId(getAppOrAdminUserId());
//        Class<?> callerClass = exp.getCallerClass();
//        Method callerMethod = exp.getCallerMethod();
//        //如果调用者类为空或者调用者方法为空 则不赋值即可
//        boolean notNull = callerClass != null && callerMethod != null;
//        if (notNull) {
//            findTagAndOperation(exp, callerMethod, callerClass);
//        }
//        //推送至业务飞书群
//        devBusiness(code, exp);
//    }
//
//    /***
//     * 手动添加异常监控，发送至飞书
//     * 该方法无需传入功能描述（title），可以自动获取接口名，只需要提前定义好如下两点：
//     * 1、需要手动在类上添加 @Tag(name = "xxx")注解
//     * 2、在方法上添加 @Operation(summary = "xxx")注解
//     *
//     * @param code      错误编码
//     * @param msg       异常信息
//     */
//    @ApiModelProperty("手动调用时调用该方法，自动填充title和uri且无请求参数")
//    public void insert(Integer code, String msg) {
//        insert(code, msg, null);
//    }
//
//    /***
//     * 手动添加异常监控，发送至飞书
//     * 手动传入功能描述（title）
//     * 但无请求地址
//     */
//    @ApiModelProperty("手动调用时调用该方法，手动传入title和uri")
//    public void insert(ExpBO req) {
//        GlobalExceptionBO exp = new GlobalExceptionBO(req, exceptionNoticeProperties);
//        //系统相关参数
//        exp.setServerName(serverName);
//        exp.setUserId(getAppOrAdminUserId());
//        //如果调用者类为空或者调用者方法为空 则不赋值即可
//        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        if (ObjectUtil.isNotNull(requestAttributes)) {
//            exp.setIp(getForwardedIp(requestAttributes.getRequest()));
//        }
//        if (Objects.equals(req.getTitleEnum().getCode(), PrometheusTitleEnum.OTHER.getCode())) {
//            //推送至【开发】飞书群
//            devSend(exp);
//        } else {
//            //推送至【业务】飞书群
//            devBusiness(req.getTitleEnum().getCode(), exp);
//        }
//    }
//
//    @ApiModelProperty("该方法会保留原格式msg")
//    public void insert(ExpBO req, Scene scene) {
//        GlobalExceptionBO exp = new GlobalExceptionBO(req, exceptionNoticeProperties);
//        //系统相关参数
//        exp.setServerName(serverName);
//        exp.setUserId(getAppOrAdminUserId());
//        //如果调用者类为空或者调用者方法为空 则不赋值即可
//        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        if (ObjectUtil.isNotNull(requestAttributes)) {
//            exp.setIp(getForwardedIp(requestAttributes.getRequest()));
//        }
//        //推送至业务飞书群
//        devBusiness(req.getTitleEnum().getCode(), exp, scene);
//    }
//
//    /***
//     * 系统相关参数
//     * @param point
//     * @param startTime
//     * @param exp
//     */
//    private void expSysInfo(JoinPoint point, long startTime, GlobalExceptionBO exp) {
//        exp.setServerName(serverName);
//        exp.setUserId(getAppOrAdminUserId());
//        exp.setReqData(getParameter(point));
//        exp.setConsumingTime((System.currentTimeMillis() - startTime) + " millis");
//    }
//
//    private String getAppOrAdminUserId() {
//        return "-";
//    }
//
//    /***
//     * 请求相关参数
//     * @param sign
//     * @param exp
//     */
//    private void expRequestInfo(MethodSignature sign, GlobalExceptionBO exp) {
//        findTagAndOperation(exp, sign.getMethod(), sign.getDeclaringType());
//    }
//
//    private void findTagAndOperation(GlobalExceptionBO exp, Method method, Class<?> targetClass) {
//        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        if (ObjectUtil.isNotNull(requestAttributes)) {
//            HttpServletRequest request = requestAttributes.getRequest();
//            exp.setIp(getForwardedIp(request));
//            exp.setUri(getApiRequestUri(targetClass, method, request.getRequestURI()));
//        }
//        //接口描述
//        if (StrUtil.isNotBlank(exp.getTitle())) {
//            return;
//        }
////        Tag tag = targetClass.getAnnotation(Tag.class);
////        if (ObjUtil.isNotNull(tag)) {
////            exp.setMonitorType(tag.name());
////        }
////        Operation operation = method.getAnnotation(Operation.class);
////        if (ObjUtil.isNotNull(operation)) {
////            exp.setTitle(operation.summary());
////        }
//    }
//
//    /***
//     * 推送至开发飞书群
//     * @param exp
//     */
//    private void devSend(GlobalExceptionBO exp) {
//        Set<Groups> groups = exceptionNoticeProperties.getDeveloper();
//        for (Groups group : groups) {
//            //发送mq异步消息，并推送至对应[飞书]群
//            send(exp, group.getUrl(), Scene.DEV);
//        }
//    }
//
//    /***
//     * 推送至业务飞书群
//     * @param code
//     * @param exp
//     */
//    private void devBusiness(Integer code, GlobalExceptionBO exp) {
//        for (Groups group : exceptionNoticeProperties.getBusiness()) {
//            //是否是需要发送至对应群的错误码
//            if (group.getCode().contains("," + code + ",")) {
//                //发送mq异步消息，并推送至对应[飞书]群
//                //在消息的另一边记录数据
//                send(exp, group.getUrl(), Scene.BUSINESS);
//            }
//        }
//    }
//
//    private void devBusiness(Integer code, GlobalExceptionBO exp, Scene scene) {
//        for (Groups group : exceptionNoticeProperties.getBusiness()) {
//            //是否是需要发送至对应群的错误码
//            if (group.getCode().contains("," + code + ",")) {
//                //发送mq异步消息，并推送至对应[飞书]群
//                //在消息的另一边记录数据
//                send(exp, group.getUrl(), scene);
//            }
//        }
//    }
//
//    /***
//     * feign的uri
//     * @param targetClass
//     * @param url
//     * @return
//     */
//    private String getApiRequestUri(Class<?> targetClass, Method method, String url) {
//        //FeignClient feignClient = targetClass.getAnnotation(FeignClient.class);
//        //if (feignClient != null) {
//        //    StringBuilder builder = new StringBuilder("/");
//        //    builder.append(feignClient.name());
//        //    builder.append("/");
//        //    builder.append(feignClient.path());
//        //    PostMapping postMapping = method.getAnnotation(PostMapping.class);
//        //    GetMapping getMapping = method.getAnnotation(GetMapping.class);
//        //    RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
//        //    if (postMapping == null && getMapping == null && requestMapping == null) {
//        //        return url;
//        //    }
//        //    String[] value = new String[]{};
//        //    if (postMapping != null) {
//        //        value = postMapping.value();
//        //    }
//        //    if (getMapping != null) {
//        //        value = getMapping.value();
//        //    }
//        //    if (requestMapping != null) {
//        //        value = requestMapping.value();
//        //    }
//        //    if (value.length <= 0) {
//        //        return url;
//        //    }
//        //    builder.append(value[0].startsWith("/") ? value[0] : "/" + value[0]);
//        //    builder.append(",feign[");
//        //    builder.append(targetClass.getSimpleName());
//        //    builder.append("]");
//        //    url = builder.toString().replaceAll("//", "/");
//        //}
//        return url;
//    }
//
//    /***
//     * 获取nginx 代理ip x-forwarded-for
//     * @param request
//     * @return
//     */
//    private String getForwardedIp(HttpServletRequest request) {
//        String ip = request.getHeader("x-forwarded-for");
//        if (StrUtil.isNotBlank(ip)) {
//            String[] values = ip.split(",");
//            if (values.length > 0) {
//                ip = values[0];
//            }
//        }
//        return StrUtil.isBlank(ip) ? request.getRemoteAddr() : ip;
//    }
//
//
//    private String getParameter(JoinPoint point) {
//        //if (((MethodSignature) point.getSignature()).getMethod().isAnnotationPresent(IgnoreRequestLog.class)) {
//        //    return null;
//        //}
//        List<String> params = new ArrayList<>();
//        Object[] args = point.getArgs();
//        for (Object obj : args) {
//            if (obj instanceof HttpServletResponse) {
//                continue;
//            }
//            if (obj instanceof HttpServletRequest) {
//                continue;
//            }
//            if (obj instanceof MultipartFile) {
//                continue;
//            }
//            if (obj instanceof BindingResult) {
//                continue;
//            }
//            params.add(JSONUtil.toJsonStr(obj));
//        }
//        return params.toString();
//    }
//
//    /***
//     * 发送mq
//     * 同步发送
//     *
//     * @param globalExp 全局异常
//     * @param scene       环境
//     */
//    private void send(GlobalExceptionBO globalExp, String groupUrl, Scene scene) {
//        globalExp.setGroupUrl(groupUrl);
//        globalExp.setScene(scene.toString());
////        Message message = new Message();
////        message.setContent(globalExp);
////        message.setTopic(CommonConstant.EXP_MQ_TOPIC);
////        message.setTags(CommonConstant.EXP_MQ_TAGS);
////        producer.syncSend(message);
//    }
//}
